import base64
import json
import requests
from base64 import b64decode
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5 as PKCS1_cipher
from ..utils.config_reader import read_util
from ..utils.logger import logger
from rsa import core, PublicKey, transform
from ..utils.server_utils import server_utils
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding

MAX_DECRYPT_BLOCK = 128  # 对于 RSA, 一般取决于密钥长度


class ApiCryptoUtils:
    """
        先产生模数因子
        然后生成rsa公钥
        再使用rsa公钥去解密传入的加密str
        """

    def __init__(self):
        # 使用公钥字符串求出模数和因子
        self._modulus = None  # 模数
        self._exponent = None  # 因子
        # 使用PublicKey(模数,因子)算出公钥
        self._pub_rsa_key = None
        self.sessions = requests.Session()
        self.base_url = read_util.read_data()['接口域名地址']['学魁图书app预生产地址']

    def _gen_modulus_exponent(self, s):
        # 对字符串解码, 解码成功返回 模数和指数
        b_str = base64.b64decode(s)
        if len(b_str) < 162:
            return False
        hex_str = b_str.hex()
        # 找到模数和指数的开头结束位置
        m_start = 29 * 2
        e_start = 159 * 2
        m_len = 128 * 2
        e_len = 3 * 2
        self._modulus = int(hex_str[m_start:m_start + m_len], 16)
        self._exponent = int(hex_str[e_start:e_start + e_len], 16)

    def _gen_rsa_pubkey(self):
        # 将pub key string 转换为 pub rsa key
        try:
            rsa_pubkey = PublicKey(self._modulus, self._exponent)
            self._pub_rsa_key = rsa_pubkey.save_pkcs1()
        except Exception as e:
            raise e

    def decode(self, encrypt_text):
        """
        decrypt msg by public key
        """
        try:
            public_key = PublicKey.load_pkcs1(self._pub_rsa_key)
            # 确保加密文本是正确的Base64格式
            while len(encrypt_text) % 4 != 0:
                encrypt_text += '='
            b64decoded_encrypt_text = base64.b64decode(encrypt_text)
            length = len(b64decoded_encrypt_text)
            length_max = 128
            decrypt_data_text = []
            data = b''
            if length >= 128:
                for i in range(0, length, length_max):
                    encrypted = transform.bytes2int(b64decoded_encrypt_text[i:i + length_max])
                    decrypted = core.decrypt_int(encrypted, public_key.e, public_key.n)
                    decrypted_bytes = transform.int2bytes(decrypted)
                    data += decrypted_bytes[decrypted_bytes.find(b'\x00') + 1:]
                try:
                    decrypt_data_text.append(data.decode())
                except Exception as e:
                    raise e
            else:
                encrypted = transform.bytes2int(b64decoded_encrypt_text)
                decrypted = core.decrypt_int(encrypted, public_key.e, public_key.n)
                decrypted_bytes = transform.int2bytes(decrypted)
                try:
                    decrypt_data_text.append(decrypted_bytes[decrypted_bytes.find(b'\x00') + 1:].decode())
                except Exception as e:
                    raise e
            if decrypt_data_text:
                return "".join(decrypt_data_text)
            else:
                return 'null'
        except Exception as ex:
            logger.error(f"An unexpected error occurred: {ex}")
            return None

    # 解密接口响应数据
    def decrypt_res_by_public_key(self, encrypt_text):
        """
        先产生模数因子
        然后生成rsa公钥
        再使用rsa公钥去解密
        """
        self._gen_modulus_exponent(read_util.read_data()['测试环境公钥'])
        self._gen_rsa_pubkey()
        ret = self.decode(encrypt_text)
        return json.loads(ret)

    # 解密接口请求数据
    @staticmethod
    def decrypt_req_by_private_key(encrypted_text):
        # 将私钥字符串转换成字节，并添加 PEM 开头和结尾
        private_key_pem = f"-----BEGIN PRIVATE KEY-----\n{read_util.read_data()['测试环境私钥']}\n-----END PRIVATE KEY-----"

        # 从字符串中加载私钥
        private_key = RSA.import_key(private_key_pem)

        # 使用私钥创建解密器
        cipher_rsa = PKCS1_cipher.new(private_key)

        # Base64 解码加密数据
        encrypted_data = b64decode(encrypted_text)

        # 对数据分段解密
        decrypted_data = bytearray()
        input_len = len(encrypted_data)
        offset = 0

        while input_len - offset > 0:
            if input_len - offset > MAX_DECRYPT_BLOCK:
                chunk = encrypted_data[offset:offset + MAX_DECRYPT_BLOCK]
                decrypted_chunk = cipher_rsa.decrypt(chunk, None)
            else:
                chunk = encrypted_data[offset:]
                decrypted_chunk = cipher_rsa.decrypt(chunk, None)

            decrypted_data.extend(decrypted_chunk)
            offset += MAX_DECRYPT_BLOCK

        return decrypted_data.decode('utf-8')

    # 加密请求数据调用客户端接口
    @staticmethod
    def encrypt_by_public_key(data):
        # 从 read_util 中读取公钥数据并将其编码为字节
        public_key_bytes = (
            f"-----BEGIN PUBLIC KEY-----\n{read_util.read_data()['测试环境公钥']}\n-----END PUBLIC KEY-----").encode('utf-8')

        # 加载公钥
        public_key = serialization.load_pem_public_key(
            public_key_bytes,
            backend=default_backend()
        )

        # 将数据编码为字节
        data_bytes = data.encode('utf-8')

        # 加密结果
        encrypted_data = bytearray()

        # 分块加密
        max_encrypt_block = 117  # 使用117字节的最大块大小，适用于PKCS1v15
        for offset in range(0, len(data_bytes), max_encrypt_block):
            block = data_bytes[offset:offset + max_encrypt_block]
            try:
                encrypted_block = public_key.encrypt(
                    block,
                    padding.PKCS1v15()  # 改用PKCS1v15填充
                )
                encrypted_data.extend(encrypted_block)
            except Exception as e:
                print("加密当前块失败:", e)
                raise

        # 返回 Base64 编码的字符串
        return base64.b64encode(encrypted_data).decode('utf-8')

    def app_post_request(self, url, data, log=True):
        """统一的 POST 请求方法"""
        app_url = self.base_url + url
        try:
            # logger.debug(f"接口请求地址{app_url},请求参数{data}")
            # 将字典转换为JSON字符串
            json_data = json.dumps(data)
            # 加密接口请求数据
            encrypted_data = self.encrypt_by_public_key(json_data)
            # 请求接口
            response = self.sessions.post(app_url, data=encrypted_data)
            # 检查响应状态码
            response.raise_for_status()
            # 解密接口响应
            result = self.decrypt_res_by_public_key(response.text)
            if log:
                # 打印接口日志
                server_utils.log_api_request_response(app_url, data, result)
                # 返回结果
                return result
            else:
                return result
        except Exception as e:
            server_utils.handle_exception(e, f"接口地址{url}请求失败,请求参数{data}")
            return None

    @staticmethod
    def prepare_data(data_dict, additional_data=None, build="1.2.0", platform_name="ANDRIOD", app_v="BOOKS"):
        """
        准备请求数据，包括公共参数和额外的参数。
        :param build: App版本号
        :param data_dict: 基础数据字典
        :param additional_data: 额外的数据字典，用于覆盖或添加基础数据
        :param platform_name: ANDRIOD/IOS
        :param app_v: app包 BOOKS/XKB
        """
        data_dict.update({
            "appUUId": read_util.read_data()['app_uid'],
            "deviceId": server_utils.generate_random_string(rando_name="", length=32),
            "build": build,
            "token": read_util.read_data()['app_token'],
            "platformName": platform_name,
            "appV": app_v
        })
        if additional_data:
            data_dict.update(additional_data)
        return data_dict


crypto_utils = ApiCryptoUtils()
